#include "preferences.h"
#include "main.h"

// initialize the application
IMPLEMENT_APP(MainApp);

const wxEventType wxEVT_THREAD = wxNewEventType();
//wxDEFINE_EVENT( wxEVT_THREAD, wxCommandEvent );

////////////////////////////////////////////////////////////////////////////////
// application class implementation 
////////////////////////////////////////////////////////////////////////////////

bool MainApp::OnInit()
{
	//wxInitAllImageHandlers();
	// init XPM image handler
	wxImage::AddHandler(new wxXPMHandler);
	
	SetTopWindow( new MainFrame( NULL ) );
	GetTopWindow()->Show();
	
	// true = enter the main loop
	return true;
}

////////////////////////////////////////////////////////////////////////////////
// main application frame implementation 
////////////////////////////////////////////////////////////////////////////////

MainFrame::MainFrame(wxWindow *parent) 
: MainFrameBase( parent ),
	m_cTrayIcon( this ),
	m_pcThread( NULL )
{
	wxGetEnv( wxT("HOME"), &m_sTutorDir );

	// check %USERPROFILE%
	if( m_sTutorDir.empty() )
		wxGetEnv( wxT("USERPROFILE"), &m_sTutorDir );

	m_sTutorDir += wxT("/.tanotutor");
	if( !wxDirExists( m_sTutorDir ) )
	{
		//create folder
		wxMkdir( m_sTutorDir, 0777 );
	}
	
	m_sSoundDir = m_sTutorDir + wxT("/sound");
	if( !wxDirExists( m_sSoundDir ) )
	{
		//create folder
		wxMkdir( m_sSoundDir, 0777 );
	}

	if( !wxDirExists( m_sTutorDir ) || !wxDirExists( m_sSoundDir ) )
	{
		// can't create directories
		exit( 0 );
	}

	m_sConfigFile = m_sTutorDir + wxT("/tanotutor.cfg");
	
	m_sTutorSoundDir = wxT("/usr/share/tanotutor/sounds/");
	
	m_lMaxWordNum = 10;
	m_lWordCount = 0;
	m_lScoreIncrement = 10;
	m_bSound = true;
	m_lSleepPeriod = 3600 * 1000; // 1 hour
	m_lCardNum = 0;
	
	ParseConfigFile();
	
	m_pcDbLink = new DbLink( m_sTutorDir, &m_lCardNum );

	m_pcDictDb  = m_pcDbLink->GetDictDb();
	m_pcCardDb  = m_pcDbLink->GetCardDb();
	m_pcWordSet = m_pcDbLink->GetWordSet();
	m_pcCardSet = m_pcDbLink->GetCardSet();

	// update mute button	
	if( !m_bSound )
	{
		wxBitmap bitmap1 = m_bpButtonMute->GetBitmapLabel();
		wxBitmap bitmap2 = m_bpButtonMute->GetBitmapDisabled();
		m_bpButtonMute->SetBitmapLabel( bitmap2 );
		m_bpButtonMute->SetBitmapDisabled( bitmap1 );
	}
	
	m_bFirstRun = true;
	
	// connect thread to main frame
	this->Connect( wxID_ANY, wxEVT_THREAD, wxCommandEventHandler( MainFrame::OnThreadEvent ) );
	
	// connect context menu to the panel of main frame
	this->Connect( m_panel->GetId(), wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( MainFrame::OnContextMenu ) );
	
	// connect context menu to the static texts
	this->Connect( m_staticTextWord->GetId(), wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( MainFrame::OnContextMenu ) );
	this->Connect( m_staticText1->GetId(),    wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( MainFrame::OnContextMenu ) );
	this->Connect( m_staticText2->GetId(),    wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( MainFrame::OnContextMenu ) );
	this->Connect( m_staticText3->GetId(),    wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( MainFrame::OnContextMenu ) );
	
	// create context menu
	m_pcContextMenu = new wxMenu();
	wxMenuItem* menuItemExit = new wxMenuItem( m_pcContextMenu, wxID_CONTEXT_MENU, wxString( _("Exit") ) );
	menuItemExit->SetBitmap( wxArtProvider::GetBitmap( wxART_QUIT ) );\
	m_pcContextMenu->Append( menuItemExit );
	
	// connect exit item to context menu
	this->Connect( wxID_CONTEXT_MENU, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainFrame::OnExitClick ) );
			
	wxIconBundle iconBundle( wxICON( tt_16x16 ) );
	iconBundle.AddIcon( wxICON( tt_32x32 ) );

	this->SetIcons( iconBundle );

	// set application icon for Windows
	this->SetIcon( wxIcon( tt_16x16_xpm ) );
}

void MainFrame::OnPreferencesClick( wxCommandEvent& WXUNUSED( event ) )
{
	Preferences pref( m_sTutorDir, m_lMaxWordNum, m_lScoreIncrement, m_lSleepPeriod, m_lCardNum );
	if( pref.ShowModal() == wxID_OK )
	{
		m_lMaxWordNum = pref.GetMaxWordNum();
		m_lScoreIncrement = pref.GetScoreIncrement();
		m_lSleepPeriod = pref.GetSleepPeriod();
		m_lCardNum = pref.GetCardNum();
	}
}

bool MainFrame::Show( bool show )
{
	if( show )
	{
		//ResetData();
		GetTranslation();
	}
	
	return MainFrameBase::Show( show );
}

MainFrame::~MainFrame()
{
	DeleteThread();	
	
	UpdateConfigFile();
	
	if( m_pcDbLink )
	{		
		delete m_pcDictDb; 
		m_pcDbLink = NULL;
	}
	
	// disconnect from thread
	this->Disconnect( wxID_ANY, wxEVT_THREAD, wxCommandEventHandler( MainFrame::OnThreadEvent ) );	
	
	// disconnect from context menu
	this->Disconnect( m_panel->GetId(), wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( MainFrame::OnContextMenu ) );
	
	// disconnect from exit item
	this->Disconnect( wxID_CONTEXT_MENU, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainFrame::OnExitClick ) );	
	
	// disconnect context menu from static texts
	this->Disconnect( m_staticTextWord->GetId(), wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( MainFrame::OnContextMenu ) );
	this->Disconnect( m_staticText1->GetId(),    wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( MainFrame::OnContextMenu ) );
	this->Disconnect( m_staticText2->GetId(),    wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( MainFrame::OnContextMenu ) );
	this->Disconnect( m_staticText3->GetId(),    wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( MainFrame::OnContextMenu ) );
}

void MainFrame::ParseConfigFile()
{
	wxTextFile file( m_sConfigFile );
	if( file.Exists() )
	{
		if( file.Open() )
		{
			size_t lineCount = file.GetLineCount();
			
			if( lineCount % 2 )
			{
				file.Close();
				return;
			}
				
			for( size_t i = 0; i < lineCount; i = i + 2 )
			{
				if( file[i] == wxT("[MaxWordNumToLearn]") )
					file[i + 1].ToLong( &m_lMaxWordNum );
				else if( file[i] == wxT("[ScoreIncrement]") )
					file[i + 1].ToLong( &m_lScoreIncrement );
				else if( file[i] == wxT("[Sound]") )
				{
					long temp;
					file[i + 1].ToLong( &temp );
					m_bSound = (bool)temp;
				}
				else if( file[i] == wxT("[BreakTime]") )
					file[i + 1].ToLong( &m_lSleepPeriod );
				else if( file[i] == wxT("[LearnOneCard]") )
					file[i + 1].ToLong( &m_lCardNum );					
			}
			file.Close();
		}
	}
}

void MainFrame::UpdateConfigFile()
{
	wxTextFile file( m_sConfigFile );
  file.Create();
	file.Close();
	if( file.Open() )
  {
		file.Clear();
		
		wxString str;
		
		str << m_lMaxWordNum;
		file.AddLine( wxT("[MaxWordNumToLearn]") );
		file.AddLine( str );
		
		str.clear();
		str << m_lScoreIncrement;
		file.AddLine( wxT("[ScoreIncrement]") );
		file.AddLine( str );
		
		str.clear();
		str << m_bSound;
		file.AddLine( wxT("[Sound]") );
		file.AddLine( str );
		
		str.clear();
		str << m_lSleepPeriod;
		file.AddLine( wxT("[BreakTime]") );
		file.AddLine( str );	
		
		str.clear();
		str << m_lCardNum;
		file.AddLine( wxT("[LearnOneCard]") );
		file.AddLine( str );	
		
		file.Write();
    file.Close();
	}
}

void MainFrame::OnCloseFrame( wxCloseEvent& WXUNUSED( event ) )
{
	//Destroy();
	// drop count for words
	m_lWordCount = 0;
	HideAndSleep( false );
}

void MainFrame::OnExitClick( wxCommandEvent& WXUNUSED( event ) )
{
	Destroy();
}

void MainFrame::OnOpenCardClick( wxCommandEvent& WXUNUSED( event ) )
{	
	assert( m_pcDictDb->IsOpen() );
	assert( m_pcCardDb->IsOpen() );
	
	CardList cCardList( m_pcDbLink, m_sSoundDir );	
	cCardList.Load();
	cCardList.ShowModal();
}

void MainFrame::OnOpenDictClick( wxCommandEvent& WXUNUSED( event ) )
{	
	assert( m_pcDictDb->IsOpen() );
	assert( m_pcCardDb->IsOpen() );
	
	DictList cDictList( m_pcDbLink, m_sSoundDir );	
	cDictList.Load();
	cDictList.ShowModal();
}

void MainFrame::OnAboutClick( wxCommandEvent& WXUNUSED( event ) )
{
	AboutDialog about( this );
	about.ShowModal();
}

void MainFrame::OnMuteClick( wxCommandEvent& WXUNUSED( event ) )
{
	wxBitmap bitmap1 = m_bpButtonMute->GetBitmapLabel();
	wxBitmap bitmap2 = m_bpButtonMute->GetBitmapDisabled();
	m_bpButtonMute->SetBitmapLabel( bitmap2 );
	m_bpButtonMute->SetBitmapDisabled( bitmap1 );
	
	m_bSound = 1 - m_bSound;
	
	m_textCtrlTran->SetFocus();
}

void MainFrame::ResetData()
{	
	m_sWordId.clear();
	m_sDictTable.clear();
	m_sCardTable.clear();
	m_lWordCount = 0;
}

void MainFrame::OnKeyDown( wxKeyEvent& event )
{
	if( event.GetKeyCode() == WXK_ESCAPE )
	{
		// drop count for words
		m_lWordCount = 0;
		HideAndSleep( false );		
	}
	else
		event.Skip();
}

void MainFrame::OnTranKeyDown( wxKeyEvent& event )
{
	int key = event.GetKeyCode();
	
	if( key == WXK_NUMPAD_ENTER || key == WXK_RETURN )
	{
		OnOkClick( (wxCommandEvent&) event );
	}
	else if( key == WXK_ESCAPE )
	{
		// drop count for words
		m_lWordCount = 0;
		HideAndSleep( false );		
	}
	else
		event.Skip();
}

void MainFrame::OnLearnCardClick( wxCommandEvent& WXUNUSED( event ) )
{
	ResetData();
	m_pcWordSet->Finalize();
	m_pcCardSet->Finalize();
	m_bFirstRun = true;
	GetTranslation();
}

void MainFrame::HideAndSleep( bool resetData )
{
	if( resetData )
		ResetData();
		
	m_statusBar->SetStatusText( wxT("") );
	Hide();
	
	// set icon
	m_cTrayIcon.SetIcon();
	
	assert( m_pcThread == 0 );
	
	m_pcThread = new Thread( this );
		
	if ( m_pcThread->Create() != wxTHREAD_NO_ERROR )
	{
		wxLogError(_("Can't create the thread!"));
		delete m_pcThread;
		m_pcThread = NULL;
	}
	else
	{
		if (m_pcThread->Run() != wxTHREAD_NO_ERROR )
		{
			wxLogError(_("Can't create the thread!"));
			delete m_pcThread;
			m_pcThread = NULL;
		}
	}
		
}

void MainFrame::OnOkClick( wxCommandEvent& WXUNUSED( event ) )
{
	// check for a word
	if( m_lWordCount == 0 )
		return;
		
	// get next word
	static bool bNextWord;
	
	if( bNextWord )
	{
		bNextWord = 1 - bNextWord;
		GetTranslation();
		return;
	}
	
	wxString res = _T("Incorrect. Score: ");
	bool bCorrect = false;
	
	for( int i = 0; i < m_textCtrlTran->GetNumberOfLines(); i++ )
	{
		wxString sTran = m_textCtrlTran->GetLineText(i);
		
		if( sTran.length() < 2 )
			continue;
		
		if( m_sTran.Find( sTran ) != -1 )
		{
			res = _("Correct. Score: ");
			m_lScore += m_lScoreIncrement;
			
			clip( m_lScore );
			
			wxString sScore;
			sScore << m_lScore;
			
			// update score
			// skip update after cards or dicts deletion
			if( !m_pcDbLink->IsNewSets() )
				m_pcCardDb->ExecuteQuery( wxT("UPDATE ") + m_sCardTable + wxT(" SET score = ") + sScore + wxT(" WHERE dicttable = '") + m_sDictTable + wxT("' AND id = ") + m_sWordId );
	
			bCorrect = true;
	
			break;
		}
	}		
	
	res << m_lScore;
	
	m_textCtrlTran->SetValue( m_sTran );
	m_statusBar->SetStatusText( res );
	
	if( m_bSound )
	{
		wxString file;
		if( bCorrect )
			file = wxT("correct");
		else
			file = wxT("incorrect");
		
		file = BaseList::GetFileName( m_sTutorDir, file );
		
		// check default path for sounds
		if( file.empty() )
			file = BaseList::GetFileName( m_sTutorSoundDir, file );
			
		BaseList::PlayFile( file );
	}
	
	bNextWord = true;
	m_buttonOk->SetFocus();
}

void MainFrame::GetTranslation()
{
	assert( m_pcCardDb->IsOpen() );
	
	wxString s;
	m_staticTextWord->SetLabel( s );
	m_textCtrlTran->SetValue( s );
	m_statusBar->SetStatusText( s );
	
	m_textCtrlTran->SetFocus();

	if( m_lWordCount >= m_lMaxWordNum )
	{
		HideAndSleep();
		return;
	}
	
	bool bNextCard = false;
	bool bNewCard = false;
	
	if( m_bFirstRun || m_pcDbLink->IsNewSets() )
	{
		m_bFirstRun = false;
		bNewCard = true;
		bNextCard = true;	
	}
	else
	{
		if( m_pcWordSet->Eof() )
			bNextCard = true;
		else
		{
			if( !m_pcWordSet->NextRow() )
			{
				bNextCard = true;					
			
				if( !m_pcCardSet->NextRow() && m_lWordCount > 0 )
				{
					HideAndSleep();
					
					// all cards have been processed
					return;
				}
			}			
		}
				
		if( m_pcCardSet->Eof() )
			bNewCard = true;
	}
			
	if( bNewCard )
	{
		m_pcDbLink->QueryNewCards();		
		
		// check for empty db
		if( !m_pcCardSet->NextRow() && m_lWordCount == 0 )
		{
			wxMessageDialog( NULL, _("No cards to learn"), _("Info"), wxOK | wxICON_INFORMATION ).ShowModal();
			return;	
		}
	}
		
	if( bNextCard )
	{
		for( ; ; )
		{
			m_sCardTable = m_pcCardSet->GetAsString(0);

			if( m_sCardTable.empty() )
			{
				wxMessageDialog( NULL, _("No cards to learn"), _("Info"), wxOK | wxICON_INFORMATION ).ShowModal();
					return;			
			}

			wxString sCardName = m_pcCardSet->GetAsString(1);
		
			m_staticTextName->SetLabel( sCardName );
		
			wxString query = wxT("SELECT * FROM ") + m_sCardTable + wxT(" WHERE score < 100 ORDER BY RANDOM() LIMIT ") ;
			query << m_lMaxWordNum;
		
			m_pcDbLink->ExecuteQueryForWordSet( query );
		
			if( m_pcWordSet->NextRow() )
				break;
			else
			{
				wxMessageDialog( NULL, _("Card ") + sCardName + _(": No words to learn"), _("Info"), wxOK | wxICON_INFORMATION ).ShowModal();
				m_pcCardSet->NextRow();				
			}
		}
	}
	
	/*
	if( m_sCardTable.empty() )
	{
		wxMessageDialog( NULL, _("No cards to learn"), _("Info"), wxOK | wxICON_INFORMATION ).ShowModal();
			return;			
	}
	*/
		
	m_sWordId    = m_pcWordSet->GetAsString(0);
	m_sDictTable = m_pcWordSet->GetAsString(1);
	m_lScore     = m_pcWordSet->GetInt(2);
			
	if( !m_sWordId.empty() && !m_sDictTable.empty() )
	{
		wxSQLite3ResultSet cSet = m_pcDictDb->ExecuteQuery( wxT("SELECT word, tran FROM ") + m_sDictTable + wxT(" WHERE id = ") + m_sWordId );
				
		wxString sWord = cSet.GetAsString(0);
		m_sTran = cSet.GetAsString(1);
		
		cSet.Finalize();
				
		m_staticTextWord->SetLabel( sWord );
		
		if( m_bSound )
		{
			wxString file = m_sSoundDir + wxT("/") + m_sDictTable;
			file = BaseList::GetFileName( file, m_sWordId );
			BaseList::PlayFile( file );
		}
				
		m_lWordCount++;
	}
	
	//if( m_lWordCount == 0 )
	//	wxMessageDialog( NULL, _("Card ") + m_staticTextName->GetLabel() + _(": No words to learn"), _("Info"), wxOK | wxICON_INFORMATION ).ShowModal();
			
	return;
}

void MainFrame::OnThreadEvent( wxCommandEvent& WXUNUSED( event ) )
{
	if( !this->IsVisible() )
	{
		// remove icon
		m_cTrayIcon.RemoveIcon();
		
		Show();
	}
}

void MainFrame::OnShowEvent( wxCommandEvent& WXUNUSED( event ) )
{
	assert( !IsVisible() );

	DeleteThread();

	// remove icon
	m_cTrayIcon.RemoveIcon();
		
	Show();
}

void MainFrame::DeleteThread()
{
	if( m_pcThread )
	{
		if( m_pcThread->Delete() != wxTHREAD_NO_ERROR )
			wxLogError(_("Can't delete the thread!"));
			
		while( 1 )
		{
			if( !m_pcThread )
				break;
			m_pcThread->Sleep(1);	
		}
	}
}

void MainFrame::OnContextMenu( wxContextMenuEvent& WXUNUSED( event ) )
{
	PopupMenu( m_pcContextMenu );
}